/*
 * Copyright (c) 2008, Xiong Yingfei, the University of Tokyo All rights
 * reserved.
 */
package ac.jp.u_tokyo.SyncLib;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;

public abstract class DynamicCombinatorBase extends StateSync
{

	private static class SyncInvokeState
	{
		private Set<Sync> _syncInvoked = new HashSet<Sync>();

		public void revert()
		{
			for (Sync s : _syncInvoked)
			{
				s.revert();
			}
		}

		public void cancelRevertPoint()
		{
			for (Sync s : _syncInvoked)
			{
				s.cancelRevertPoint();
			}
		}
	}

	private Stack<SyncInvokeState> _state = new Stack<SyncInvokeState>();

	private Set<Sync> _syncInvokedInCurrentSession = null;

	public DynamicCombinatorBase(Object[] initialState)
	{
		super(initialState);
	}

	@Override
	public void cancelRevertPoint()
	{
		super.cancelRevertPoint();
		_state.pop().cancelRevertPoint();
	}

	@Override
	public void revert()
	{
		super.revert();
		_state.pop().revert();
	}

	@Override
	public void setRevertPoint()
	{
		super.setRevertPoint();
		_state.push(new SyncInvokeState());
	}

	protected final Mod[] invokeSync(Sync s, Mod[] mods)
			throws SynchronizationFailedException
	{
		if (!_syncInvokedInCurrentSession.contains(s))
		{
			s.setRevertPoint();
			_syncInvokedInCurrentSession.add(s);
		}
		return s.synchronize(mods);
	}
	
	protected final Mod[] invokeResync(Sync s, Object[] values, Mod[] mods)
			throws SynchronizationFailedException
	{
		if (!_syncInvokedInCurrentSession.contains(s))
		{
			s.setRevertPoint();
			_syncInvokedInCurrentSession.add(s);
		}
		return s.resynchronize(values, mods);
	}

	protected abstract Mod[] doResynchronize(Object[] values,
			Mod[] modifications) throws SynchronizationFailedException;

	private void afterSuccessfulSynchronization()
	{
		if (!_state.empty())
		{
			for (Sync s : _syncInvokedInCurrentSession)
				if (_state.peek()._syncInvoked.contains(s))
				{
					_syncInvokedInCurrentSession.remove(s);
					_state.peek()._syncInvoked.add(s);
				}
		}
		for (Sync s : _syncInvokedInCurrentSession)
		{
			s.cancelRevertPoint();
		}
		_syncInvokedInCurrentSession = null;
	}

	private void afterFailedSynchronization()
	{
		for (Sync s : _syncInvokedInCurrentSession)
		{
			s.revert();
		}
		_syncInvokedInCurrentSession = null;
	}

	private void beforeSynchronization()
	{
		_syncInvokedInCurrentSession = new HashSet<Sync>();
	}

	public final Mod[] resynchronize(Object[] values, Mod[] modifications)
			throws SynchronizationFailedException
	{
		beforeSynchronization();
		try
		{
			Mod[] result = doResynchronize(values, modifications);
			afterSuccessfulSynchronization();
			return result;
		}
		catch (SynchronizationFailedException e)
		{
			afterFailedSynchronization();
			throw e;
		}
	}

	protected abstract Mod[] doSynchronize(Mod[] modifications)
			throws SynchronizationFailedException;

	public final Mod[] synchronize(Mod[] modifications)
			throws SynchronizationFailedException
	{
		beforeSynchronization();
		try
		{
			Mod[] result = doSynchronize(modifications);
			afterSuccessfulSynchronization();
			return result;
		}
		catch (SynchronizationFailedException e)
		{
			afterFailedSynchronization();
			throw e;
		}
	}

}
